package com.liuyi.framework.web.encrypt;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.alibaba.fastjson.JSONObject;
import com.liuyi.tools.SpringContext;
import com.liuyi.tools.kits.RequestKit;
import com.liuyi.tools.web.unusual.BizEnum;
import lombok.Data;

import javax.crypto.SecretKey;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

/**
 * <p>
 * 加密解密工具类
 * </p>
 *
 * @author Mr.Fmy
 * @since 2021-02-01
 */
public class EncryptionTool {

    private static EncryptionKeyStored encryptionKeyStored;
    private static String version_key;
    private final static ThreadLocal<RSA> RSA_THREADLOCAL = new ThreadLocal<>();

    static {
        encryptionKeyStored = SpringContext.getBean(EncryptionKeyStored.class);
        version_key = SpringContext.getEnv().getProperty("spring.crypto.request.decrypt.versionKey", "crypto_version");
    }

    private static RSA getRsa() {
        return getRsa(RequestKit.getRequest());
    }

    private static RSA getRsa(HttpServletRequest request) {
        if (RSA_THREADLOCAL.get() == null) {
            String version = request.getHeader(version_key);
            Rsa rsa;
            if (StrUtil.isBlank(version)) {
                rsa = encryptionKeyStored.getRsa();
            } else {
                Integer lastVersion = encryptionKeyStored.getLastVersion();
                if (lastVersion.compareTo(Convert.toInt(version)) > 0) {
                    throw new CryptoRequestException(BizEnum.CRYPTO_REQUEST_VERSION_LAST);
                }
                rsa = encryptionKeyStored.getRsa(Convert.toInt(version));
            }
            if (rsa == null) {
                throw new IllegalArgumentException("crypto.request.decrypt.version：" + version + "，not query RSA");
            }
            RSA_THREADLOCAL.set(new RSA(rsa.getPrivateKey(), rsa.getPublicKey()));
        }
        return RSA_THREADLOCAL.get();
    }

    /**
     * 解密
     */
    public static byte[] decrypt(String rsaKey, InputStream in, String charsName) {
        try {
            RSA rsa = getRsa();
            String body = IoUtil.read(in, charsName);
            JSONObject jsonObject = JSONObject.parseObject(body);
            SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DES, rsa.decrypt(rsaKey, KeyType.PrivateKey));
            return des.decrypt(jsonObject.getString("crypto"));
        } finally {
            RSA_THREADLOCAL.remove();
        }
    }

    /**
     * 加密
     */
    public static Encrypt encrypt(String res, String charsName) throws UnsupportedEncodingException {
        try {
            RSA rsa = getRsa();
            byte[] key = IdUtil.simpleUUID().getBytes(charsName);
            String rsaKey = rsa.encryptBase64(key, KeyType.PublicKey);
            SymmetricCrypto des = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
            String ciphertext = des.encryptBase64(res.getBytes(charsName));
            Encrypt encrypt = new Encrypt();
            encrypt.setCiphertext(ciphertext);
            encrypt.setRsaKey(rsaKey);
            return encrypt;
        } finally {
            RSA_THREADLOCAL.remove();
        }
    }

    @Data
    public static class Encrypt {

        private String ciphertext;

        private String rsaKey;
    }
}
